﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ufida.u9.ait.openapi.utils;
using UFIDA.U9.Base;
using UFIDA.U9.Base.BaseBE.ValueSetDefBE;
using UFIDA.U9.Base.Currency;
using UFIDA.U9.Base.Doc;
using UFIDA.U9.Base.UOM;
using UFIDA.U9.CBO.FI.Tax;
using UFIDA.U9.CBO.HR.Department;
using UFIDA.U9.CBO.HR.Operator;
using UFIDA.U9.CBO.SCM.Customer;
using UFIDA.U9.CBO.SCM.Enums;
using UFIDA.U9.CBO.SCM.Item;
using UFIDA.U9.CBO.SCM.ProjectTask;
using UFIDA.U9.CBO.SCM.Warehouse;
using UFIDA.U9.ER.ERBP;
using UFIDA.U9.SM.Enums;
using UFIDA.U9.SM.RMA;
using UFIDA.U9.SM.Ship;
using UFSoft.UBF.Business;

namespace ufida.u9.ait.openapi
{
    internal class RMABase
    {
        public static RMA CreateDoc(RMAAITDTOData dto)
        {
            RMA doc = null;

            using(ISession session = Session.Open())
            {
                doc = CreateHead(dto);
                CreateLines(doc, dto);

                session.Commit();
            }

            return doc;
        }

        private static void CreateLines(RMA doc, RMAAITDTOData dto)
        {
            if (dto.RMALines == null || dto.RMALines.Count <= 0) throw new Exception("行集合必填");

            foreach(var linedto in dto.RMALines)
            {
                RMALine line = RMALine.Create(doc);
                //默认值
                LineDefault(line,linedto);
                //来源单据处理
                LineSrcDoc(line, linedto);
                //数量/单位
                LineQty(linedto, line);
                //单价金额处理
                LinePriceMny(doc, linedto, line);
                
                //创建税子行
                LineTax(line);
            }
        }
        /// <summary>
        /// 行扩展字段处理
        /// </summary>
        /// <param name="linedto"></param>
        /// <param name="line"></param>
        private static void LineDesc(RMALineAITDTOData linedto, RMALine line)
        {
            if (linedto.DescFlexField != null)
            {
                line.DescFlexField = new UFIDA.U9.Base.FlexField.DescFlexField.DescFlexSegments();
                DTOUtils.DTOPropCopy(linedto.DescFlexField, line.DescFlexField);
            }
        }

        /// <summary>
        /// 数量/单位 处理
        /// </summary>
        /// <param name="linedto"></param>
        /// <param name="line"></param>
        private static void LineQty(RMALineAITDTOData linedto, RMALine line)
        {
            ItemMaster item = line.ItemInfo.ItemID;
            RMA doc = line.RMA;

            line.TU = item.SalesUOM;//交易单位
            line.BaseTU = item.SalesUOM.BaseUOM;
            line.PU = item.PriceUOM;//计价单位
            line.BasePU = item.PriceUOM.BaseUOM;
            line.IU = item.InventorySecondUOM;//库存单位
            line.IBU = item.InventorySecondUOM.BaseUOM;
            line.IMU = item.InventoryUOM;//库存主单位
            if (doc.SrcDocType == RMASrcDocTypeEnum.Ship)
            {
                //来源=出货单
                ShipLine shipline = ShipLine.Finder.FindByID(line.SrcDocLine.ID);
                if (shipline == null) throw new Exception("查找出货单失败");

                line.TU = shipline.TradeUom;//交易单位
                line.BaseTU = shipline.TradeBaseUom;
                line.PU = shipline.PriceUom;//计价单位
                line.BasePU = shipline.PriceBaseUom;
                line.IU = shipline.InvUom;//库存单位
                line.IBU = shipline.InvBaseUom;
                line.IMU = shipline.InvMainUOM;//库存主单位
            }
            //如果DTO传入,则直接覆盖
            if (linedto.TU != null)
            {
                //销售单位
                UOM uom = EntityUtils.GetEntityByCode<UOM>(linedto.TU.Code);
                line.TU = uom;
                line.BaseTU = uom.BaseUOM;
            }
            if (linedto.PU != null)
            {
                //计价单位
                UOM uom = EntityUtils.GetEntityByCode<UOM>(linedto.PU.Code);
                line.PU = uom;
                line.BasePU = uom.BaseUOM;
            }
            if (linedto.IU != null)
            {
                //库存单位
                UOM uom = EntityUtils.GetEntityByCode<UOM>(linedto.IU.Code);
                line.IU = uom;
                line.IBU = uom.BaseUOM;
            }
            if (linedto.IMU != null)
            {
                //库存主单位
                UOM uom = EntityUtils.GetEntityByCode<UOM>(linedto.IMU.Code);
                line.IMU = uom;
            }

            //数量
            if (linedto.RtnQtyTU1 == 0) throw new Exception("RtnQtyTU1必填");
            line.RtnQtyTU1 = linedto.RtnQtyTU1;//核定数量(交易单位)
            line.RtnQtyPU = linedto.RtnQtyPU > 0 ? linedto.RtnQtyPU : line.RtnQtyTU1;//核定数量(计价单位)
            line.RtnQtyIU = linedto.RtnQtyIU > 0 ? linedto.RtnQtyIU : line.RtnQtyTU1;//核定数量(库存单位)
            
            line.ApplyQtyTU1 = line.RtnQtyTU1;//申请数量(交易单位)
            line.ApplyQtyPU = line.RtnQtyPU;//申请数量(计价单位)
            line.ApplyQtyIU = line.RtnQtyIU;//申请数量(库存单位)

            //转换率
            line.TBUToIBURate = Math.Round(line.RtnQtyIU / line.RtnQtyTU1,5); //销售基准单位-库存基准单位
            line.BaseTUToBasePURate = Math.Round(line.RtnQtyPU / line.RtnQtyTU1,5);//交易-计价
        }

        /// <summary>
        /// 单价/金额/税额 处理
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="linedto"></param>
        /// <param name="line"></param>
        private static void LinePriceMny(RMA doc, RMALineAITDTOData linedto, RMALine line)
        {
            ItemMaster item = line.ItemInfo.ItemID;
            //税组合
            LineTaxSchedule(doc, linedto, line, item);
            //含税单价
            line.RtnPice = linedto.RtnPice;
            line.OrderPrice = line.RtnPice;
            line.ApplyPrice = line.RtnPice;
            //含税金额
            if (linedto.RtnMoneyTC > 0)
            {
                line.RtnMoneyTC = linedto.RtnMoneyTC;
            }
            else
            {
                line.RtnMoneyTC = line.RtnQtyPU * line.RtnPice;
            }
            line.RtnMoneyFC = line.RtnMoneyTC;
            line.RtnMoneyAC = line.RtnMoneyTC;
            line.ApplyMoneyTC = line.RtnMoneyTC;
            //不含税金额
            line.RtnMoneyTCNet = line.RtnMoneyTC / (1 + line.TaxRate);
            line.RtnMoneyTCNet = U9Utils.GetRoundMoney(doc.TC, line.RtnMoneyTCNet);
            line.RtnMoneyFCNet = line.RtnMoneyTCNet;
            line.RtnMoneyACNet = line.RtnMoneyTCNet;
            line.ApplyNetMoneyTC = line.RtnMoneyTCNet;

            //不含税单价
            line.RtnPriceNet = line.RtnMoneyTCNet / line.RtnQtyPU;
            line.RtnPriceNet = U9Utils.GetRoundPrice(doc.TC, line.RtnPriceNet);

            //税额
            line.TotalTaxTC = line.RtnMoneyTC - line.RtnMoneyTCNet;
            line.TotalTaxFC = line.TotalTaxTC;
            line.TotalTaxAC = line.TotalTaxTC;
        }

        /// <summary>
        /// 创建税子行
        /// </summary>
        /// <param name="line"></param>
        private static void LineTax(RMALine line)
        {
            LineTax tax = UFIDA.U9.SM.RMA.LineTax.Create(line);
            tax.TotalTaxTC = line.TotalTaxTC;
            tax.TotalTaxFC = line.TotalTaxFC;
            tax.TotalTaxAC = line.TotalTaxAC;
            tax.TaxRate = line.TaxRate;
            tax.Org = line.Org;
            tax.TaxType = line.TaxSchedule.TaxScheduleTaxs[0].Tax;
        }

        private static void LineTaxSchedule(RMA doc, RMALineAITDTOData linedto, RMALine line, ItemMaster item)
        {
            if (line.TaxSchedule == null)
            {
                if (linedto.TaxSchedule != null)
                {
                    //优先传入
                    line.TaxSchedule = EntityUtils.GetEntityByCode<TaxSchedule>(linedto.TaxSchedule.Code);
                }
                else if (item.SaleInfo.SaleTaxSchedule != null)
                {
                    //其次料品
                    line.TaxSchedule = item.SaleInfo.SaleTaxSchedule;
                }
                else if (doc.Customer.Customer.TaxSchedule != null)
                {
                    //其次客户
                    line.TaxSchedule = doc.Customer.Customer.TaxSchedule;
                }
                if (line.TaxSchedule == null) throw new Exception("TaxSchedule为空");
            }
            line.TaxRate = line.TaxSchedule.TaxScheduleTaxs[0].Tax.TaxRate;//税率
        }

        private static void LineDefault(RMALine line, RMALineAITDTOData linedto)
        {
            RMA doc = line.RMA;

            //行号
            if (linedto.DocLineNo > 0)
            {
                line.DocLineNo = linedto.DocLineNo;
            }
            else
            {
                line.DocLineNo = doc.RMALines.Count*10;
            }

            //物料
            if (linedto.ItemInfo == null) throw new Exception("料号必填");
            ItemMaster item = EntityUtils.GetEntityByCode<ItemMaster>(linedto.ItemInfo.Code);
            line.ItemInfo = new ItemInfo()
            {
                ItemID = item,
                ItemCode = item.Code,
                ItemName = item.Name,
            };
            //存储地点
            line.Warehouse = EntityUtils.GetEntityByCode<Warehouse>(linedto.Warehouse);

            //组织
            line.Org = doc.Org;
            line.ShipperOrg = doc.Org;
            line.RcvOrg = doc.Org;
            line.InvoiceOrg = doc.Org;
            line.AccountOrg = doc.Org;
            line.ConsignorOrg = doc.Org;
            if (doc.RMAType == RMRProcessTypeEnum.ExchangeRMA)
            {
                //调换RMA
                line.RtnFillOrg = doc.Org;//补货组织
            }else if (doc.RMAType == RMRProcessTypeEnum.OncreditRMA)
            {
                //赊账RMA
                line.RcvOrgKey = null;//收货组织
            }
            //转换率
            line.TCToFCExchRate = 1;
            line.TCToACExchRate = 1;
            
            line.TUToBaseTURate = 1;//交易-基准
            line.PUToBasePURate = 1;//计价-基准
            line.IUToIBURate = 1;//库存单位-库存基准单位
            line.IBUToIMURate = 1;//库存基准单位-库存主单位
            line.BaseTUToBasePURate = 1;//交易-计价
            line.TBUToIBURate = 1;//销售基准单位-库存基准单位
            //币种
            line.TC = doc.TC;
            line.FC = doc.FC;
            line.AC = doc.AC;

            //扩展字段
            LineDesc(linedto, line);

            //其他
            line.Project = EntityUtils.GetEntityByCode<Project>(linedto.Project);//项目
            line.Status = doc.Status;
            line.StateMachineID = Guid.Empty;
            line.SrcSeller = doc.Seller;
            line.SrcSaleDept = doc.SaleDept;
            line.SrcDocBusinessDate = DateTime.MinValue;
            line.SeibanAlterable = SeibanAlterableEnum.GetFromValue(3);
            line.RtnFillBy = RtnFillByEnum.GetFromValue(1);
            line.PriceSource = PriceSourceEnum.GetFromValue(1);//价格来源=手工
            line.PayerSite = doc.ShipToSite;
            line.Payer = doc.Customer;
            line.CreditObject = doc.ShipToSite;
            line.BillToSite = doc.BillToSite;
            line.MergeAR = doc.MergeAR;
            line.IsTCToACExchRateChangeable = true;
            line.IsManufacturerChangeable = true;
            line.IsEngrossCreditLimit = true;
            line.IsChangeableItemVersion = true;
            line.IsApplyPriceChangeable = true;
            line.IsTaskChangeable = true;
            line.IsProjectChangeable = true;
            line.IsAutoRevaluation = true;
            line.IsTCToACExchRateChangeable = true;
            line.IsApplyPriceChangeable = true;
            line.DiscountRate = 1;//折扣率
            line.IsAutoToRcv = doc.DocumentType.IsAutoToRcv;//自动收货
            line.IsAutoToRcvRtn=doc.DocumentType.IsAutoToRcvRtn;//自动采购退货
        }

        private static void LineSrcDoc(RMALine line,RMALineAITDTOData linedto)
        {
            RMA doc = line.RMA;
            line.SrcDocType = doc.SrcDocType;
            if (doc.SrcDocType == RMASrcDocTypeEnum.Ship)
            {
                //来源出货单
                if (string.IsNullOrEmpty(linedto.SrcDocNo)) throw new Exception("来源单号为空");
                if (linedto.SrcDocLineNo <= 0) throw new Exception("来源行号为空");

                string path = $"DocLineNo={linedto.SrcDocLineNo} and Ship.DocNo='{linedto.SrcDocNo}'";
                ShipLine shipLine = ShipLine.Finder.Find(path);
                if (shipLine == null) throw new Exception($"查询出货单失败!path:[{path}]");
                if (shipLine.ItemInfo.ItemCode != linedto.ItemInfo.Code)
                    throw new Exception($"来源单据物料编码[{shipLine.ItemInfo.ItemCode}]与传入物料编码[{linedto.ItemInfo.Code}]不同!");

                Ship ship = shipLine.Ship;

                doc.SrcDocNo = linedto.SrcDocNo;
                line.SrcSoDocNo = shipLine.SONo;
                line.SrcSoDoc = shipLine.SOKey;
                line.SrcShipOrg = ship.Org;
                //line.SrcDocSysVer = shipLine.SysVersion;//报错 出货单已经做过修改，数据不致，不能处理
                line.SrcDocSysVer = -1;
                line.SrcDocOwnedOrg = ship.Org;
                line.SrcDocNo = ship.DocNo;
                line.SrcDocLineNo = shipLine.DocLineNo;
                line.SrcDocLine = shipLine;
                line.SrcDocBusinessDate = ship.BusinessDate;
                line.SrcDoc = ship;
                line.PriceSource = PriceSourceEnum.GetFromValue(3);//价格来源
                line.OperatingOrg = shipLine.Org;//原接单组织

                line.ParSrcDocType = RMAParSrcDocTypeEnum.GetFromValue(1);//源头单据类型
                line.ParSrcDocOrg = ship.Org;
                line.ParSrcDocNo = ship.DocNo;
                line.ParSrcDocLineNo = shipLine.DocLineNo;
                line.ParSrcDocLine = shipLine.ID;
                line.ParSrcDoc = ship.ID;
                line.ParSrcDocBizType = BusinessTypeEnum.GetFromValue(449);//源头单据业务类型

                line.CostPrice = shipLine.SaleCostTC / shipLine.SaleCostAmount;//成本价
                line.TaxSchedule=shipLine.TaxSchedule;//税组合
                //line.Warehouse = shipLine.WH;//存储地点
                if (shipLine.Project != null)
                {
                    line.Project= shipLine.Project;//项目
                }
            }
        }

        private static RMA CreateHead(RMAAITDTOData dto)
        {
            RMA doc = RMA.Create();
            doc.DocNo = "自动编号";
            if (string.IsNullOrEmpty(dto.BusinessDate))
                throw new Exception("业务日期必填");
            doc.BusinessDate = DateTime.Parse(dto.BusinessDate);//业务日期
            doc.RequireDate = doc.BusinessDate;
            if (dto.DocumentType == null)
                throw new Exception("单据类型必填");
            doc.DocumentType = EntityUtils.GetEntityByCode<RMADocType>(dto.DocumentType);//单据类型
            //币种                                                                      
            if (dto.TC == null)
            {
                doc.TC = EntityUtils.GetEntityByCode<Currency>("C001");
            }
            else
            {
                doc.TC = EntityUtils.GetEntityByCode<Currency>(dto.TC);
            }
            doc.FC = doc.TC;
            doc.AC = doc.TC;

            //组织
            doc.Org = Context.LoginOrg;
            doc.SettlementOrg = doc.Org;
            doc.InvoiceOrg = doc.Org;
            doc.AccountOrg = doc.Org;

            //客户
            if (dto.Customer == null)
                throw new Exception("客户必填");
            Customer cu = EntityUtils.GetEntityByCode<Customer>(dto.Customer);
            if (cu.CustomerSites == null || cu.CustomerSites.Count == 0)
                throw new Exception($"客户[{cu.Code}]不存在客户位置");
            CustomerSite site = cu.CustomerSites[0];
            doc.Customer = new CustomerMISCInfo(cu, cu.Code, cu.Name, cu.ShortName);
            doc.ShipToSite = new CustomerSiteMISCInfo(site, site.Code, site.Name);
            doc.BillToSite = doc.ShipToSite;

            //扩展字段
            if (dto.DescFlexField != null)
            {
                doc.DescFlexField = new UFIDA.U9.Base.FlexField.DescFlexField.DescFlexSegments();
                DTOUtils.DTOPropCopy(dto.DescFlexField, doc.DescFlexField);
            }

            //其它
            doc.Status = RMAStatusEnum.Open;//状态
            doc.Seller = EntityUtils.GetEntityByCode<Operators>(dto.Seller);//业务员
            doc.SaleDept = EntityUtils.GetEntityByCode<Department>(dto.SaleDept);//部门
            doc.MergeAR = doc.DocumentType.MergeAR; //合并立账
            doc.KeepAccountsPeriod = EntityUtils.getPostPeriod(doc.BusinessDate);//记账期间
            doc.InvoiceAccording = doc.DocumentType.InvoiceAccording;//开票依据
            doc.ConfirmAccording = doc.DocumentType.AccountAccording;//立账依据
            doc.AccrueTerm = site.ARConfirmTerm;//立账条件
            doc.ACToFCExchRate = 1;
            doc.SrcDocType = RMASrcDocTypeEnum.GetFromValue(dto.SrcDocType);//来源单据类别
            doc.RMAType = doc.DocumentType.RMAType;//退回类型
            
            return doc;
        }

        public static RMA CommitDoc(RMA doc)
        {
            doc = doc.Key.GetEntity();
            if (doc.Status != RMAStatusEnum.Open)
                throw new Exception($"单号：[{doc.DocNo}]状态不为开立！");

            RmaStatusOperateBP bp = new RmaStatusOperateBP();
            bp.OperateType = RMAStatusEnum.Posting;
            bp.DocHead = new RMAUIVerDTO(doc.SysVersion, doc.Key);
            bp.Do();
            return doc;
        }

        public static RMA ApproveDoc(RMA doc)
        {
            //清除缓存,否则不会自动推送收货单
            U9Utils.ClearCache();
            doc = doc.Key.GetEntity();
            if (doc.Status != RMAStatusEnum.Posting)
                throw new Exception($"单号：[{doc.DocNo}]状态不为提交！");

            RmaStatusOperateBP bp = new RmaStatusOperateBP();
            bp.OperateType = RMAStatusEnum.Posted;//已核准
            bp.DocHead = new RMAUIVerDTO(doc.SysVersion, doc.Key);
            bp.Do();
            return doc;
        }

        public static RMA RevocateDoc(RMA doc)
        {
            doc = doc.Key.GetEntity();
            if (doc.Status != RMAStatusEnum.Posting)
                throw new Exception($"单号：[{doc.DocNo}]状态不为提交，不能收回！");

            using (ISession session = Session.Open())
            {
                doc.Status = RMAStatusEnum.Open;
                foreach (RMALine line in doc.RMALines)
                {
                    line.Status = RMAStatusEnum.Open;
                }
                session.Commit();
            }
            return doc;
        }
    }
}
